#include "pythonvoid.h"

#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <limits.h>

typedef struct
{
	PyObject obj;
	int value;
} PyInt;

typedef struct
{
	PyObject obj;
	long value;
} PyLong;

typedef struct
{
	PyObject obj;
	size_t length;
	char data[0];
} PyString;

#define PyString_AS_STRING(ptr) PyString_AsString( (PyObject*) ptr )
char* PyString_AsString( PyObject* obj )
{
	assert( obj->type == PY_STRING );
	PyString* str = (PyString*) obj;
	return str->data;
}

void Py_DECREF( PyObject* obj )
{
	assert( obj != NULL );
	assert( obj->refcount > 0 );
	obj->refcount --;

	if ( obj->refcount == 0 )
	{
		free( obj );
		//~ printf( stderr, "DECREF: refcount = 0 (%p)\n", obj );
	}
}

void Py_XDECREF( void* obj )
{
	if ( obj != NULL )
	{
		Py_DECREF( (PyObject*) obj );
	}
}

void Py_INCREF( PyObject* obj )
{
	assert( obj != NULL );
	obj->refcount ++;
	
	printf( "INCREF: refcount = %zu (%p)\n", obj->refcount, obj );
}

size_t PyString_Size( const PyObject* obj )
{
	assert( obj->type == PY_STRING );
	PyString* strObj = (PyString*) obj;
	
	return strObj->length;
}

PyObject* PyString_FromStringAndSize( const char* str, size_t len )
{
	PyString* strObj = (PyString*) malloc( sizeof(PyString) + len + 1 );
	if ( strObj == NULL ) return NULL;
		
	assert( (void*) strObj == (void*) &(strObj->obj) );
	
	strObj->obj.type = PY_STRING;
	strObj->obj.refcount = 1;
	strObj->length = len;
	
	if ( str != NULL )
	{
		// TODO: Use strlcpy if available, or provide our own implementation
		//~ strlcpy( strObj->data, str, len+1 );
		strncpy( strObj->data, str, len );
		// strncpy sucks: make sure the string is NULL terminated
		strObj->data[len] = '\0';
		
	}
	else
	{
		strObj->data[0] = '\0';
	}
	
	return (PyObject*) strObj;
}

PyObject* PyString_InternFromString( const char* str )
{
	return PyString_FromStringAndSize( str, strlen(str) );
}

int PyString_Check( PyObject* obj )
{
	assert( obj != NULL );
	assert( obj->refcount > 0 );
	
	if ( obj->type != PY_STRING ) {
		return 0;
	}
	
	PyString* strObj = (PyString*) obj;
	assert( strObj->data[strObj->length] == '\0' );
	assert( strObj->length == strlen( strObj->data ) );
	
	return 1;
}

void PyString_InternInPlace( PyObject** obj )
{
	assert( PyString_Check(*obj) );
}

void PyString_ConcatAndDel( PyObject** destination, PyObject* source )
{
	assert( PyString_Check(*destination) );
	assert( PyString_Check(source) );
	
	PyString* str1 = (PyString*) *destination;
	PyString* str2 = (PyString*) source;
	
	PyString* strobj = (PyString*) PyString_FromStringAndSize( str1->data, str1->length + str2->length );
	strcat( strobj->data, str2->data );
	
	Py_DECREF( *destination );
	Py_DECREF( source );
	
	*destination = (PyObject*) strobj;
}

// Returns < 1 if there is an error
int _PyString_Resize( PyObject** destination, size_t length )
{
	assert( PyString_Check( *destination ) );
	
	PyString* str = (PyString*) *destination;
	assert( length > str->length );
	
	PyObject* newstr = PyString_FromStringAndSize( str->data, length );
	
	Py_DECREF( *destination );
	*destination = newstr;
	return 0;
}

PyObject* PyLong_FromString(char* str, char **pend, int base)
{
	printf( "long string = %s %p %d\n", str, pend, base );
	assert(0);
	return NULL;
	
	//~ PyLong* longobj = (PyLong*) malloc( sizeof(PyLong) );
	//~ longobj->obj.type = PY_LONG;
	//~ longobj->obj.refcount = 1;
	//~ longobj->value = 0;
	
	//~ return (PyObject*) longobj;
}

PyObject* PyInt_FromLong(long value)
{
	assert( INT_MIN <= value && value <= INT_MAX );
	PyInt* intobj = (PyInt*) malloc( sizeof(PyInt) );
	intobj->obj.type = PY_INT;
	intobj->obj.refcount = 1;
	intobj->value = (int) value;
	
	assert( (long) intobj->value == value );
	
	return (PyObject*) intobj;
}

int PyInt_Check(PyObject* obj)
{
	return obj->type == PY_INT;
}

int PyInt_Value(PyObject* obj)
{
	assert( PyInt_Check( obj ) );
	
	PyInt* intobj = (PyInt*) obj;
	return intobj->value;
}


PyObject* PyFloat_FromDouble(double value)
{
	printf( "double parse = %f\n", value );
	assert(0);
	return NULL;
}

PyObject* PyUnicode_DecodeRawUnicodeEscape(
    const char *string,         /* Raw-Unicode-Escape encoded string */
    int length,                 /* size of string */
    const char *errors          /* error handling */
    )
{
	assert(0);
	return NULL;
}

PyObject* PyUnicode_DecodeUnicodeEscape(
    const char *string,         /* Unicode-Escape encoded string */
    int length,                 /* size of string */
    const char *errors          /* error handling */
    )
{
	assert(0);
	return NULL;
}

void PyErr_BadInternalCall()
{
	assert(0);
	abort();
}

void PyErr_NoMemory()
{
	assert(0);
	abort();
}

PyObject* PyString_DecodeEscape(const char *s,
                                int len,
                                const char *errors,
                                int unicode,
                                const char *recode_encoding)
{
	assert(0);
	return NULL;
}
